using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using SpoonCore2.Client;
using System.Timers;
using SpoonCore2.Message;
using System.IO;
using System.Threading;
using SpoonCore2.Tools;
using Timer = System.Timers.Timer;

namespace SpoonCore2.Service.Lookup
{
	public class LookupService : AbstractService
	{
		private static readonly SpoonIdentifier _id = SpoonIdentifier.Parse("52E7F0F6DF97AEACFD1781586D6E3450");
		private readonly LookupDatabaseMemory _db = new LookupDatabaseMemory();
		private readonly SortedList<int, ILoadItem> _waiterList = new SortedList<int, ILoadItem>();
		private ReplicationManager<LookupMessage> _repManager;
		private readonly SortedList<SpoonIdentifier, CheckItem> _checkList = new SortedList<SpoonIdentifier, CheckItem>();

		public override SpoonIdentifier Id { get { return _id; } }

		public delegate void LoadCompleteHandler<T>(T item);

		public override void Dispose()
		{
		}

		public override void Init()
		{
			Handler.Register(new MessageHandler.ReceiveMessageHandler<LookupMessage>(LookupReceive));
			_repManager = new ReplicationManager<LookupMessage>(Check, Refresh);
		}

		private void Refresh(SpoonIdentifier replicationId, LookupMessage item)
		{
			
		}

		private void Check(SpoonIdentifier replicationId, ReplicationManager<LookupMessage>.CheckCompleteHandle handler)
		{
			CheckItem cItem = new CheckItem(replicationId, handler);
			_checkList.Add(replicationId, cItem);

			LookupMessage msg = new LookupMessage();
			msg.RequestType = LookupMessage.RequestTypeValue.Request;
			msg.StorageType = LookupMessage.StorageTypeValue.Check;
			msg.LookupId = replicationId;
			//handler(replicationId, item, ReplicationManager<LookupMessage>.CheckStatus.Ok);
		}

		private void LookupReceive(ClientInfo sender, LookupMessage msg)
		{
			if (msg.Sender == null) msg.Sender = sender;
			if (msg.Sender.Endpoints.Length == 0) msg.Sender.AddEndpoint(sender.Endpoints);

			if (msg.RequestType == LookupMessage.RequestTypeValue.Request)
			{
				if (Manager.Network.Successor(msg.LookupId))
				{
					if (msg.StorageType == LookupMessage.StorageTypeValue.Store)
					{
						_db.Set(msg.TypeId, msg.LookupId, msg.Data);
					}
					else if (msg.StorageType == LookupMessage.StorageTypeValue.Load)
					{
						byte[] data = _db.Get(msg.TypeId, msg.LookupId);

						LookupMessage res = new LookupMessage();
						res.Id = msg.Id;
						res.RequestType = LookupMessage.RequestTypeValue.Response;
						res.StorageType = LookupMessage.StorageTypeValue.Load;
						res.Data = data;

						msg.Sender.Send(res);
					}
					else if(msg.StorageType == LookupMessage.StorageTypeValue.Check)
					{
						bool status = _db.Contrains(msg.TypeId, msg.LookupId);
						if(status)
						{
							msg.RequestType = LookupMessage.RequestTypeValue.Response;
							msg.Sender.Send(msg);
						}
					}
				}
				else
				{
					// Forward message
					Send(msg.LookupId, msg);
				}
			}
			else if (msg.RequestType == LookupMessage.RequestTypeValue.Response)
			{
				if (msg.StorageType == LookupMessage.StorageTypeValue.Load && _waiterList.ContainsKey(msg.Id))
				{
					_waiterList[msg.Id].Waiter.Data = msg.Data;
					_waiterList[msg.Id].Handle();
					_waiterList.Remove(msg.Id);
				}
			}
		}

		public void Store(ILookupItem data)
		{
			data.LookupId = new SpoonIdentifier();

			LookupMessage msg = new LookupMessage();

			msg.LookupId = data.LookupId;
			msg.TypeId = data.TypeId;
			MemoryStream stream = new MemoryStream();
			BinaryWriter bw = new BinaryWriter(stream);
			data.GetBytes(bw);
			msg.Data = stream.ToArray();

			Send(data.LookupId, msg);

			_repManager.Register(msg.LookupId, msg);
		}

		public T Load<T>(SpoonIdentifier id) where T : ILookupItem, new()
		{
			LookupMessage msg = new LookupMessage();
			msg.LookupId = id;
			msg.TypeId = new T().TypeId;
			msg.RequestType = LookupMessage.RequestTypeValue.Request;
			msg.StorageType = LookupMessage.StorageTypeValue.Load;

			LoadItem<T> item = new LoadItem<T>();
			_waiterList.Add(msg.Id, item);
			Send(id, msg);

			item.Waiter.Waiter.WaitOne();

			T tmp = new T();
			tmp.SetBytes(new BinaryReader(new MemoryStream(item.Waiter.Data)));
			return tmp;
		}

		public void BeginLoad<T>(SpoonIdentifier id, LoadCompleteHandler<T> handler) where T : ILookupItem, new()
		{
			LookupMessage msg = new LookupMessage();
			msg.LookupId = id;
			msg.TypeId = new T().TypeId;
			msg.RequestType = LookupMessage.RequestTypeValue.Request;
			msg.StorageType = LookupMessage.StorageTypeValue.Load;

			LoadItem<T> item = new LoadItem<T>(handler);
			_waiterList.Add(msg.Id, item);
			Send(id, msg);
		}

		private interface ILoadItem
		{
			DataWaiter<byte[]> Waiter { get; }
			void Handle();
		}

		private class LoadItem<T> : ILoadItem where T : ILookupItem, new()
		{
			private readonly DataWaiter<byte[]> _waiter = new DataWaiter<byte[]>();
			private readonly LoadCompleteHandler<T> Handler;

			public LoadItem(LoadCompleteHandler<T> handler)
			{
				Handler = handler;
			}

			public LoadItem()
			{
				
			}

			public DataWaiter<byte[]> Waiter
			{
				get { return _waiter; }
			}

			public void Handle()
			{
				T tmp = new T();
				if (_waiter.Data != null) tmp.SetBytes(new BinaryReader(new MemoryStream(_waiter.Data)));
				if(Handler != null) Handler(tmp);
			}
		}

		private class CheckItem
		{
			public SpoonIdentifier Id;
			public ReplicationManager<LookupMessage>.CheckCompleteHandle Handler;

			public CheckItem(SpoonIdentifier id, ReplicationManager<LookupMessage>.CheckCompleteHandle handler)
			{
				Id = id;
				Handler = handler;
			}
		}

		public override void Start()
		{
			
		}

		public override void Stop()
		{
			
		}
	}
}